/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package snh;


import framework.Entidade;
import framework.Estado;
import framework.Msg;
import framework.Timeout;
import java.util.HashMap;

/**
 *
 * @author jayme
 */

public class NO extends Entidade{

    
    private String end_rede_pai;
    private int end_sn;
    private String end_rede;
    private HashMap<Integer, EntradaTabela> tabela;
    
    public Estado _wait;
    public Estado _idle;
    public Estado _nasce;
    
    Thread thread1;
    public Timeout ev_timeout;
   
    /*
     * construtor
     */
    NO(String end_rede){
        super(L_PORT);
        end_rede_pai = "";
        this.end_sn = hashCode();
        this.end_rede = end_rede;
        this._idle = new No_Idle(this);
        this._wait = new No_Wait(this);
        this._nasce = new No_Nasce(this);
        est = _nasce;
        tabela = new HashMap<Integer, EntradaTabela>();
        ev_timeout = new Timeout(this,10000);
    }

   public String rede(){
       return end_rede;
   }

   public int id(){
       return end_sn;
   }
   
  public void setEndRedePai(String end_rede_pai){
      this.end_rede_pai = end_rede_pai; 
  }
  
  public String getEndRedePai(){
      return end_rede_pai; 
  }
  

   /*
    * insere um NO na tabela e repassa a inserção Para o pai
    */
   public void insere(int end_sn, String end_rede, int status){
       //estado = INSERIR;
       EntradaTabela ent = new EntradaTabela(end_sn, end_rede, status);
       tabela.put(end_sn, ent);
      
       if (!end_rede_pai.isEmpty()){
           //estado = WAIT3;
           Msg msg;
           msg = new Msg(L_PORT);
           msg.conecta(end_rede_pai,L_PORT);
           msg.envia(INSERE+";"+end_rede+";"+end_sn+";"+ENTRADA);  
           msg.termina();
           //System.out.println("Repassei um insere");
       }
       else{///estamos na raiz
           Msg msg;
           msg = new Msg(L_PORT);
           msg.conecta(end_rede,L_PORT);
           System.out.println(end_rede);
           msg.envia(OK+";;;");
           msg.termina();
           //System.out.println("enviei OK para "+ end_rede);
       }
       //estado = IDLE;
       //return true;
   }

   public void lookup(int end_sn, String end_rede_remetente){
       if (est != _idle){
           System.out.println("Impossivel fazer lookup no estado "+est.nome);
           //return "";
       }
       else{
           //estado = RESOLVE;
           if (end_sn == id()){
               Msg msg = new Msg(L_PORT);
               msg.conecta(end_rede_remetente, L_PORT);
               msg.envia(RESPONDE+";"+rede()+";;");
               msg.termina();
               
               //estado = IDLE;
               System.out.println(RESPONDE+";"+rede()+";;");
           }
           else{
                EntradaTabela linhaTabela = tabela.get(end_sn);
                if (linhaTabela == null) {
                    //O no não tem conhecimento do endereço do sistema de nomeação
                    if (end_rede_pai.isEmpty()){
                        //a raiz tambem desconhece o endereço, logo o no não existe na rede.
                        Msg msg = new Msg(L_PORT);
                        msg.conecta(end_rede_remetente, L_PORT);
                        msg.envia(RESPONDE+"; O nó solicitado não está a rede;;");
                        msg.termina();
                        //estado = IDLE;
                        System.out.println(RESPONDE+"; O nó solicitado não está a rede;;");
                    }
                    else{
                        /**O no não tem conhecimento do endereço do sistema de nomeação
                         * mas não é a raiz então repassa para o pai
                         */
                        //estado = WAIT2;
                        Msg msg = new Msg(L_PORT);
                        msg.conecta(end_rede_pai, L_PORT);
                        msg.envia(LOOKUP+";"+end_rede_remetente+";"+end_sn+";");
                        msg.termina();
                        ///estado = IDLE;
                        System.out.println(LOOKUP+";"+end_rede_remetente+";"+end_sn+";");
                    }
                }
                else{
                    //o no tem conhecimento do endereço do sistema de nomeação
                    if (linhaTabela.tipo == FILHO){
                        //o endereço procurado é um filho direto
                        Msg msg = new Msg(L_PORT);
                        msg.conecta(end_rede_remetente, L_PORT);
                        msg.envia(RESPONDE+";"+linhaTabela.end_rede+";"+";");
                        msg.termina();
                        //estado = IDLE;
                        System.out.println(RESPONDE+";"+linhaTabela.end_rede+";"+";");
                    }
                    else{
                        //o endereço procurado não é um filho, então repassa o lookup para o filho
                        //estado = WAIT2;
                        Msg msg = new Msg(L_PORT);
                        msg.conecta(linhaTabela.end_rede, L_PORT);
                        msg.envia(LOOKUP+";"+end_rede_remetente+";"+end_sn+";");
                        msg.termina();
                        //estado = IDLE;
                        System.out.println(RESPONDE+";"+linhaTabela.end_rede+";"+";");
                    }
                }
           }
       }
   }
   

    /*
   * pede a inserção algum no da rede, esse no será o seu pai
   *
   * Uma vez que um no nasce ele deve sempre deve chamar este procedimento.
   */
   public void pedeInsercao(String end_rede){
       if (est != _nasce){
           System.out.println("Não é possível fazer a inserção do nó no estado "+est.nome);
           return;
       }
       
       if (end_rede.isEmpty()){
       //então eu sou a raiz, ou seja, o rimeiro no a ser inserido na rede.
           //estado = IDLE;
           System.out.println("Nó inserido com sucesso como raiz, id: " + id());
           
           est=_idle;
       }
       else{///pede a inserção de si proprio para o nó
           Msg msg = new Msg(L_PORT);
           msg.conecta(end_rede, L_PORT);
           /* mensagem comtem 4 informaçõaes
           1: codigo para a ação a ser executada
           2: endereço do sistema de nomeação.
           3: tipo de inserção a ser feita (FILHO OU ENTRADA)
           4: endereço de rede de quem estão solicitando a inserção
           */
           msg.envia(INSERE+";"+rede()+";"+id()+";"+FILHO);
           msg.termina();
            // muda estado para waiting
            est=_wait;
            setEndRedePai(end_rede);
             // dispara o timer
             // inicializa thread 
            thread1 = new Thread(ev_timeout);
            thread1.start();  
           //estado = WAIT;         
          // ((NO)ent).ev_timeout.run();
       }
   }
   
   
}
